package com.cmc.cloud.cmclink.doc.util;

import com.cmc.cloud.cmclink.doc.api.document.emums.ExImTypeEnum;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoFreightErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.dto.ImExVslvoyAge;
import com.cmc.cloud.cmclink.doc.entity.BlnoVslvoyDO;
import com.cmc.cloud.cmclink.doc.entity.VoyageLockDO;
import com.cmc.cloud.cmclink.doc.rpcservice.VoyScheduleService;
import com.cmc.cloud.cmclink.doc.service.si.VoyageLockService;
import com.cmc.cloud.cmclink.doc.vo.specialcargoapplyvo.SpecialCargoVoyageUpdateReqVO;
import com.cmc.cloud.cmclink.svl.api.base.enums.SystemEnums;
import com.cmc.cloud.cmclink.svl.api.base.req.VesselVoyReqDTO;
import com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 费用计算util
 * 
 * @author dev89
 * @date 2025/09/18 09:45
 **/
@Component
@Slf4j
public class CostChargeUtil {
    @Resource
    private VoyScheduleService voyScheduleService;

    @Resource
    private VoyageLockService voyageLockService;

    /**
     * 获取进口船名航次
     *
     * @param currentPort currentPort
     * @param vslvoyList vslvoyList
     * @return ImExVslvoyAge
     */
    public static ImExVslvoyAge getImVslvoyAge(List<BlnoVslvoyDO> vslvoyList, String currentPort) {
        ImExVslvoyAge imExVslvoyAge = new ImExVslvoyAge();
        Optional<BlnoVslvoyDO> blnoOptinal =
            vslvoyList.stream().filter(vslvoy -> vslvoy.getPodCode().equals(currentPort)).findFirst();
        BlnoVslvoyDO blnoVslvoyDO = blnoOptinal.orElse(null);
        if (blnoVslvoyDO == null) {
            return imExVslvoyAge;
        }
        imExVslvoyAge.setVoyage(blnoVslvoyDO.getVoyageIm());
        imExVslvoyAge.setVesselCode(blnoVslvoyDO.getVesselCode());
        return imExVslvoyAge;
    }

    /**
     * 获取出口船名航次
     *
     * @param currentPort currentPort
     * @param vslvoyList vslvoyList
     * @return ImExVslvoyAge
     */
    public static ImExVslvoyAge getExVslvoyAge(List<BlnoVslvoyDO> vslvoyList, String currentPort) {
        ImExVslvoyAge imExVslvoyAge = new ImExVslvoyAge();
        Optional<BlnoVslvoyDO> blnoOptinal =
            vslvoyList.stream().filter(vslvoy -> vslvoy.getPolCode().equals(currentPort)).findFirst();
        BlnoVslvoyDO blnoVslvoyDO = blnoOptinal.orElse(null);
        if (blnoVslvoyDO == null) {
            return imExVslvoyAge;
        }
        imExVslvoyAge.setVoyage(blnoVslvoyDO.getVoyageEx());
        imExVslvoyAge.setVesselCode(blnoVslvoyDO.getVesselCode());
        return imExVslvoyAge;
    }

    /**
     * 随机获取第一条laneCode出口
     *
     * @param vslvoyList vslvoyList
     * @param currentPort currentPort
     * @return string
     */
    public static String getExLaneCode(List<BlnoVslvoyDO> vslvoyList, String currentPort) {
        if (CollectionUtils.isEmpty(vslvoyList)) {
            return "";
        }
        Optional<BlnoVslvoyDO> blnoOptinal =
            vslvoyList.stream().filter(vslvoy -> vslvoy.getPolCode().equals(currentPort)).findFirst();
        BlnoVslvoyDO blnoVslvoyDO = blnoOptinal.orElse(null);
        if (blnoVslvoyDO == null) {
            return "";
        }
        return blnoVslvoyDO.getLaneCodeEx();
    }

    /**
     * 随机获取第一条laneCode 进口
     *
     * @param vslvoyList vslvoyList
     * @param currentPort currentPort
     * @return string
     */
    public static String getImLaneCode(List<BlnoVslvoyDO> vslvoyList, String currentPort) {
        if (CollectionUtils.isEmpty(vslvoyList)) {
            return "";
        }
        Optional<BlnoVslvoyDO> blnoOptinal =
            vslvoyList.stream().filter(vslvoy -> vslvoy.getPodCode().equals(currentPort)).findFirst();
        BlnoVslvoyDO blnoVslvoyDO = blnoOptinal.orElse(null);
        if (blnoVslvoyDO == null) {
            return "";
        }
        return blnoVslvoyDO.getLaneCodeIm();
    }

    /**
     * VesselVoyRespDTO
     *
     * @param currentPort currentPort
     * @param laneCode laneCode
     * @param vesselVoys vesselVoys
     * @param voyAge voyage
     *
     * @return com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO
     */
    public VesselVoyRespDTO getVoyQueryRespDTO(String currentPort, String voyAge, String laneCode,
        List<String> vesselVoys) {
        try {
            VesselVoyReqDTO vesselVoyReqDTO = new VesselVoyReqDTO();
            vesselVoyReqDTO.setLaneCode(laneCode);
            vesselVoyReqDTO.setPortCode(currentPort);
            vesselVoyReqDTO.setVoy(voyAge);
            vesselVoyReqDTO.setVesselCodeList(vesselVoys);
            vesselVoyReqDTO.setSystemNumber(SystemEnums.DOC.getSystemNumber());
            List<VesselVoyRespDTO> listVesselVoy = voyScheduleService.listVesselVoy(vesselVoyReqDTO);
            if (CollectionUtils.isEmpty(listVesselVoy) || listVesselVoy.get(0) == null) {
                throw ServiceExceptionUtil.exception(BlnoFreightErrorCodeConstants.BLNO_FREIGHT_ETU_IS_NULL);
            }
            return listVesselVoy.get(0);
        } catch (Exception e) {
            log.error("caught an exception on get businessDate", e);
            throw ServiceExceptionUtil.exception(BlnoFreightErrorCodeConstants.BLNO_FREIGHT_ETU_IS_NULL, e.toString());
        }
    }

    /**
     * VesselVoyRespDTO
     *
     * @param currentPort currentPort
     * @param exImType exImType
     * @param vslvoyList vslvoyList
     * @return com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO
     */
    public VesselVoyRespDTO getVoyQueryRespDTO(String currentPort, String exImType, List<BlnoVslvoyDO> vslvoyList) {
        List<String> vesselVoys = new ArrayList<>();
        ImExVslvoyAge imExVslvoyAge;
        String laneCode;
        // 获取进口或者出口航次
        if (ExImTypeEnum.EX.getValue().equalsIgnoreCase(exImType)) {
            imExVslvoyAge = CostChargeUtil.getExVslvoyAge(vslvoyList, currentPort);
            laneCode = CostChargeUtil.getExLaneCode(vslvoyList, currentPort);
        } else {
            imExVslvoyAge = CostChargeUtil.getImVslvoyAge(vslvoyList, currentPort);
            laneCode = CostChargeUtil.getImLaneCode(vslvoyList, currentPort);
        }
        vesselVoys.add(imExVslvoyAge.getVesselCode());
        return getVoyQueryRespDTO(currentPort, imExVslvoyAge.getVoyage(), laneCode, vesselVoys);
    }

    /**
     * VesselVoyRespDTO
     *
     * @param currentPort currentPort
     * @param laneCode laneCode
     * @param vesselVoys vesselVoys
     * @param voyAge voyage
     *
     * @return com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO
     */
    public LocalDateTime getBusinessDate(String currentPort, String voyAge, String laneCode, List<String> vesselVoys) {
        VesselVoyRespDTO respDTO = getVoyQueryRespDTO(currentPort, voyAge, laneCode, vesselVoys);
        String ptu = respDTO.getPtu();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        return LocalDateTime.parse(ptu, formatter);
    }

    public Map<String, String> getVoyPortTerminalMap(String laneCode, String voyage, String vessel) {
        Map<String, String> result = new HashMap<>(16);
        try {
            VesselVoyReqDTO vesselVoyReqDTO = new VesselVoyReqDTO();
            vesselVoyReqDTO.setLaneCode(laneCode);
            vesselVoyReqDTO.setVoy(voyage);
            vesselVoyReqDTO.setVesselCodeList(Arrays.asList(vessel));
            vesselVoyReqDTO.setSystemNumber(SystemEnums.DOC.getSystemNumber());
            List<VesselVoyRespDTO> listVesselVoy = voyScheduleService.listVesselVoy(vesselVoyReqDTO);
            if (CollectionUtils.isEmpty(listVesselVoy)) {
                return result;
            }
            return listVesselVoy.stream()
                .filter(e -> StringUtils.isNotEmpty(e.getPortCode()) && StringUtils.isNotEmpty(e.getTerminalCode()))
                .collect(Collectors.toMap(VesselVoyRespDTO::getPortCode, VesselVoyRespDTO::getTerminalCode));
        } catch (Exception e) {
            // 异常直接返回
            return result;
        }
    }

    public Map<String, VesselVoyRespDTO> getVesselVoyMap(String laneCode, String voyage, String vessel) {
        Map<String, VesselVoyRespDTO> result = new HashMap<>(16);
        try {
            VesselVoyReqDTO vesselVoyReqDTO = new VesselVoyReqDTO();
            vesselVoyReqDTO.setLaneCode(laneCode);
            vesselVoyReqDTO.setVoy(voyage);
            vesselVoyReqDTO.setVesselCodeList(Arrays.asList(vessel));
            vesselVoyReqDTO.setSystemNumber(SystemEnums.DOC.getSystemNumber());
            List<VesselVoyRespDTO> listVesselVoy = voyScheduleService.listVesselVoy(vesselVoyReqDTO);
            if (CollectionUtils.isEmpty(listVesselVoy)) {
                return result;
            }
            return listVesselVoy.stream().collect(Collectors.toMap(VesselVoyRespDTO::getPortCode, e -> e));
        } catch (Exception e) {
            // 异常直接返回
            return result;
        }
    }

    /**
     * 锁船判断
     *
     * @param vslvoyList vslvoyList
     * @return boolean
     */
    public boolean checkLockVoyage(List<BlnoVslvoyDO> vslvoyList) {
        BlnoVslvoyDO vslvoy = VslvoyUtil.getFirstVslvoy(vslvoyList);
        if (vslvoy == null) {
            return false;
        }
        VoyageLockDO voyageLock = voyageLockService.findVoyageLock(vslvoy.getVesselCode(), vslvoy.getVoyageEx(),
            vslvoy.getPolCode(), ExImTypeEnum.EX.getValue());
        if (voyageLock == null) {
            return false;
        }
        return voyageLock.getVoyageLockFlag();
    }

    public void setVslvoyTerminal(List<SpecialCargoVoyageUpdateReqVO> voyageList) {
        if (CollectionUtils.isNotEmpty(voyageList)) {
            voyageList.forEach(e -> {
                Map<String, String> terminalMap = getVoyPortTerminalMap(e.getLaneCode(), e.getVoyageEx(), e.getVesselCode());
                if (terminalMap != null) {
                    e.setPolTerminalCode(terminalMap.get(e.getPolCode()));
                    e.setPodTerminalCode(terminalMap.get(e.getPodCode()));
                }
            });
        }
    }
}
